<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button onclick="routerHistory.push('/')">首页</button>
    <button onclick="routerHistory.push('/about')">关于我</button>
    <button onclick="routerHistory.replace('/XXX')">替换</button>
    <script>
        //前端路由的实现原理：两种模式 一种是hash模式 一种是history模式 h5api
        //window.location.hash = "/" history.push()  history.pushState(state,null,url)  history.replaceState()
        //目前浏览器都支持了history.pushState(ie11以上)

        //两种路由的区别 
        //hash has模式的好处 就是锚点，刷新页面的时候 不会像服务器发送请求，不会出现404；缺点：不支持服务端渲染，不能做seo优化。上线不会使用太丑。 #a
        //history 特点就是漂亮 没有#和正常页面切换一样，如果刷新页面会像服务器一样发送请求，如果资源不存在会出现404，解决方法，渲染手写，首页会根据路径重新跳转

        //自己实现一个核心模块 路径 状态 前后页面是啥等信息 自己的状态管理 computedScroll A的滚动条位置
        function buildState(back, current, forward, replace = false, computedScroll = false) {
            return {
                back,
                current,
                forward,
                replace,
                scroll: computedScroll ? {
                    left: window.pageXOffset,
                    top: window.pageYOffset
                } : null,
                position: window.localStorage.length - 1 //默认是从2开始所以-1
            }
        }

        //当前地址
        function createCurrentLocation(base) {
            const {
                pathname,
                search,
                hash
            } = window.location;
            const hasPos = base.indexOf("#"); //就是hash  //about -> #/ #/about
            if (hasPos > -1) {
                return base.slice(1) || "/";
            }
            return pathname + search + hash
        }

        function useHistoryStateNavigation(base) {
            const currentLocation = {
                value: createCurrentLocation(base)
            }
            const historyState = {//状态
                    value: window.location.state
                }
                //第一次刷新页面 此时没有任何状态 那么我就自己维护一个状态（后退后是哪个路径，当前路径是哪个，要去哪里，我用push跳转还是replace跳转，跳转后滚动条位置是在哪）
            if (!historyState.value) {
                changeLocation(currentLocation.value, buildState(null, currentLocation.value, null, true), true);
                buildState(null, currentLocation.value, null, true);
            }

            function changeLocation(to, state, replace) {
                let hasPos = base.indexOf('#')
                const url = hasPos > -1 ? base + to : to;
                window.history[replace ? "replaceState" : "pushState"](state, null, to)
                historyState.value = state; //将自己生成的状态同步到 路由系统中了
            }

            function push(to, data) { //去哪里 带的新的状态是谁？
                //跳转的时候 我需要做两个状态 一个是跳转前 从哪里去哪 去哪儿的状态
                const currentState = Object.assign({}, historyState.value, {
                        forward: to,
                        scroll: {
                            left: window.pageXOffset,
                            top: window.pageYOffset
                        }
                    })
                    //本质没有跳转 只是更新了状态 后续在vue中可以监控到状态的变化
                changeLocation(currentState.current, currentState, true);
                //新的状态
                const state = Object.assign({}, buildState(currentLocation.value, to, null), {
                    position: currentState.position + 1
                }, data)
                changeLocation(to, state, false)
                currentLocation.value = to;
                //跳转后 从这到了那
            }

            function replace(to, data) {
                const state = Object.assign({}, buildState(historyState.value.back, to, historyState.value.forward, true), data)
                changeLocation(to, state, true);
                currentLocation.value = to; //替换后需要将路径变成现在的路径
            }

            return {
                replace,
                push,
                state: historyState,
                location: currentLocation
            }
        }

        //前进后退的时候要更新 historyState 和 currentLocation这两个变量
        function useHistoryListeners(base, historyState, currentLocation) {
            let listener = [];
            const popStateHandler = ({//最新的状态 已经趋近后退完毕后的状态
                state
            }) => { 
                const to = createCurrentLocation(base); //去哪里
                const from = currentLocation.value; //去哪来
                const fromState = historyState.value; //从哪来的状态
                currentLocation.value = to;
                historyState.value = state; //state 可能会为null

                //<0 说明是后退 >0说明是前进
                let isBack = state.position - fromState.position < 0
                console.log(state);

                //用户在这里拓展
                listener.forEach(listener => {
                    listener(to, from, {
                        isBack
                    })
                })
            }
            
            window.addEventListener("popstate", popStateHandler); //只能监听浏览器的前进后退
            function listen(cb) {
              listener.push(cb);
            }
            return {
              listen
            }
        }

        function createWebHistory(base = "") {
            //路由系统最基本 就得包含当前的路径 当前路径下他的状态是什么 提供路径切换方法 push replace
            //实现路由监听，如果路径变化，需要通知用户
            const historyNavigation = useHistoryStateNavigation(base);

            const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location);
            const routerHistory = Object.assign({}, historyNavigation, historyListeners)

            //代理取值方便
            Object.defineProperty(routerHistory, 'location', {
                get: () => historyNavigation.location.value
            })
            Object.defineProperty(routerHistory, 'state', {
                get: () => historyNavigation.state.value
            })

            return routerHistory
        }

        function createWebHashHistory(params) {
            return createWebHistory("#")
        }

       
        const routerHistory = createWebHashHistory();
        routerHistory.listen((to,from,{isBack})=>{
          console.log(to,from,isBack);
        })
            //实现路由监听，如果路径变化需要通知用户
    </script>
</body>

</html>